<?php

class APIDefinition_Object_Message {

  protected $_id;
  protected $_actionName;
  protected $_controllerName;
  protected $_moduleName;
  protected $_message = array();  
  protected $_creatorEmail;
  protected $_creationDate;
  protected $_description;
  protected $_status;
  protected $_versionNumber;
      
  /* definition of the message table */
  static protected $_dbMessageTableName = 'messages';
  static protected $_dbMessageIdCol = 'id';
  static protected $_dbMessageActionNameCol = 'actionName';
  static protected $_dbMessageControllerNameCol = 'controllerName';
  static protected $_dbMessageModuleNameCol = 'moduleName';
  static protected $_dbMessageMessageCol = 'message';
  static protected $_dbMessageDescriptionCol = 'description';
  static protected $_dbMessageCreatorEmailCol = 'creatorEmail';
  static protected $_dbMessageCreationDateCol = 'creationDate';  
  static protected $_dbMessageStatusCol = 'status';  
  static protected $_dbMessageVersionNumberCol = 'versionNumber'; 
  
    
  static protected $_validStatusList = array('','current','supported','deprecated');
  
  public function __construct($actionName,$controllerName,$moduleName,$id = '',$status = '',$versionNumber = '' ,$data = array(),$special = '') {
  	
  	if(empty($data)) {
   	  //We validate the name
  	  self::_validateName($actionName);
  	  self::_validateName($controllerName);  	  
  	  self::_validateName($moduleName);
  	  //We get the data from the database
      $db = Zend_Registry::get('dt_dbs')->getDb('apidefinition','message',$actionName.'_'.$controllerName.'_'.$moduleName);
      $select = $db->select()->from(self::$_dbMessageTableName);
      $select->where($db->quoteInto(self::$_dbMessageActionNameCol ." = ?", $actionName));
      $select->where($db->quoteInto(self::$_dbMessageControllerNameCol ." = ?", $controllerName));
      $select->where($db->quoteInto(self::$_dbMessageModuleNameCol ." = ?", $moduleName));
	  
	  //We look wish message is required in the following order: id, version,status. For status, we return the most current one
	  //If Id is provided, we go for this one
	  if(!empty($id)) $select->where($db->quoteInto(self::$_dbMessageIdCol ." = ?", $id));
	  elseif(!empty($versionNumber)) $select->where($db->quoteInto(self::$_dbMessageVersionNumberCol ." = ?", $versionNumber));
	  elseif(!empty($status) && in_array  ( $status , self::$_validStatusList )) {
	  	$select->where($db->quoteInto(self::$_dbMessageStatusCol ." = ?", $status));
	  	$select->order(self::$_dbMessageCreationDateCol.' DESC');
	  } elseif(!empty($special) && 'latest' == $special) {
	  	$select->order(self::$_dbMessageIdCol.' DESC');
	  	$select->limit(1,0);	   	
	  } elseif(!empty($special) && 'whatever' == $special) {
	  	//nothing, we don t really care what is the message returned (used for history purpose)	   	
	  }
	  else throw new APIDefinition_Object_Exception('Message not found',8); 

      $res = $db->fetchAll($select->__toString());
    
      if(empty($res)) {
  	    require_once('apidefinition/objects/Exception.php');
  	    throw new APIDefinition_Object_Exception('Message not found',8); 
      }
    $data = $res[0];
  	}
    $this->_id = $data[self::$_dbMessageIdCol];
    $this->_actionName = $data[self::$_dbMessageActionNameCol];
    $this->_controllerName = $data[self::$_dbMessageControllerNameCol];
    $this->_moduleName = $data[self::$_dbMessageModuleNameCol];
    $this->_description = $data[self::$_dbMessageDescriptionCol];
    $this->_creatorEmail = $data[self::$_dbMessageCreatorEmailCol];
    $this->_creationDate = $data[self::$_dbMessageCreationDateCol];
    $this->_message = self::decodeMessage($data[self::$_dbMessageMessageCol]);
    $this->_status = $data[self::$_dbMessageStatusCol];
    $this->_versionNumber = $data[self::$_dbMessageVersionNumberCol];	

      	
  }	//end constructor

  public function asArray() {
  	$results = array();
    $results['id'] = $this->_id;
    $results['actionName'] = $this->_actionName;
    $results['controllerName'] = $this->_controllerName;
    $results['moduleName'] = $this->_moduleName;
    $results['description'] = $this->_description;
    $results['creatorEmail'] = $this->_creatorEmail;
    $results['creationDate'] = $this->_creationDate;
    $results['message'] = $this->_message;
    $results['status'] = $this->_status;
    $results['versionNumber'] = $this->_versionNumber;	  	
    return $results;
  }


  public function getHistory() {
      $db = Zend_Registry::get('dt_dbs')->getDb('apidefinition','message',$this->_actionName.'_'.$this->_controllerName.'_'.$this->_moduleName);
      $select = $db->select()->from(self::$_dbMessageTableName);
      $select->where($db->quoteInto(self::$_dbMessageActionNameCol ." = ?", $this->_actionName));
      $select->where($db->quoteInto(self::$_dbMessageControllerNameCol ." = ?", $this->_controllerName));
      $select->where($db->quoteInto(self::$_dbMessageModuleNameCol ." = ?", $this->_moduleName));
      $select->order(self::$_dbMessageCreationDateCol.' DESC');
      //$select->limit(1,0);
      $res = $db->fetchAll($select->__toString());
      
      if(empty($res)) return array();
      $historyList = array();
      
      foreach($res as $num => $data) {
      	$tempMessage = new 	APIDefinition_Object_Message('','','','','','' ,$data);
      	$tempArray = $tempMessage->asArray();
      	
      	//we unset the message to not overburden the data returned
      	unset($tempArray['message']);
      	$historyList[] = $tempArray;
      	
      }
      return $historyList;
        	
  }

  public function getId() { return $this->_id;}
  public function getActionName() { return $this->_actionName;}
  public function getControllerName() { return $this->_controllerName;}
  public function getModuleName() { return $this->_moduleName;}
  public function getMessage() { return $this->_message;}  
  public function getCreatorEmail() { return $this->_creatorEmail;}
  public function getCreationDate() { return $this->_creationDate;}
  public function getDescription() { return $this->_description;}
  public function getStatus() { return $this->_status;}
  public function getVersionNumber() { return $this->_versionNumber;}
  
  //the message is stored as a base64 gzdefalted serialized PHP
  static public function decodeMessage($data) {
	
    $decoded = @unserialize(gzinflate(base64_decode($data)));
    if(empty($decoded)) {
      require_once('apidefinition/objects/Exception.php');
      throw new APIDefinition_Object_Exception('An issue occured while decoding message',16);
    }  
    return  $decoded;	
  }

  static public function encodeMessage($data) {
  	if(!is_array($data)) throw new APIDefinition_Object_Exception('input parameter is not an array',12); 
    $encoded = @base64_encode(gzdeflate(serialize($data),9));
    if(empty($encoded)) throw new APIDefinition_Object_Exception('An issue occured while encoding message',17); 
    return $encoded;   	
  }
	
  static public function listing($actionName,$controllerName,$moduleName) {
    $db = Zend_Registry::get('dt_dbs')->getDb('apidefinition','message',$actionName.'_'.$controllerName.'_'.$moduleName);
    $select = $db->select()->from(self::$_dbMessageTableName);
    $select->where($db->quoteInto(self::$_dbMessageActionNameCol ." = ?", $controllerName));
    $select->where($db->quoteInto(self::$_dbMessageControllerNameCol ." = ?", $controllerName));
    $select->where($db->quoteInto(self::$_dbMessageModuleNameCol ." = ?", $moduleName));
    $res = $db->fetchAll($select->__toString());  

    if(empty($res)) return array();

    //We must reinterpret the rows so it is independant to the way it is stored
    $results = array();
    foreach($res as $num => $row) {
    	$action = new APIDefinition_Object_Message('','','','','','',$row);
    	$results[] = $action->asArray();	
    }
    
    return $results;
      
  } //end list
  
  
  //only to be used when no message at all exists for the action
  static public function createBase($params) {

  	
  	//We clean the data provided
  	//We validate the existence of the different parent objects
    require_once('apidefinition/objects/action.php');
    $action = new APIDefinition_Object_Action($params['actionName'],$params['controllerName'],$params['moduleName']);
  	$actionName = $action->getName();

    require_once('apidefinition/objects/controller.php');
    $controller = new APIDefinition_Object_Controller($params['controllerName'],$params['moduleName']);
  	$controllerName = $controller->getName();
  	  	
    require_once('apidefinition/objects/module.php');
    $module = new APIDefinition_Object_Module($params['moduleName']);
  	$moduleName = $module->getName();
  	
    
    //We check if any Message is not already existing
    $existing = false;
    $result = APIDefinition_Object_Message::listing($actionName,$controllerName,$moduleName);
    if(!empty($result)) $existing = true;
    
    if($existing) {
  	    require_once('apidefinition/objects/Exception.php');
  	    throw new APIDefinition_Object_Exception('Message is already existing',10);     	
    }


    //OK we can create the Message
	$email = Zend_Registry::get('app_user')->getEmail();
  	$creationDate = time();
  	
  	//default message definition
  	$defaultMessage = array('inputDefinition' => array(),'outputDefinition' => array());
  	
  	$data = array(
      self::$_dbMessageActionNameCol => $actionName,
      self::$_dbMessageControllerNameCol => $controllerName,
      self::$_dbMessageModuleNameCol => $moduleName,
      self::$_dbMessageDescriptionCol => 'automatically generated at action creation',
      self::$_dbMessageCreatorEmailCol => $email,
      self::$_dbMessageCreationDateCol => $creationDate,
      self::$_dbMessageMessageCol => self::encodeMessage($defaultMessage),
      self::$_dbMessageStatusCol => 'current',
      self::$_dbMessageVersionNumberCol => 0
  	);
    $db = Zend_Registry::get('dt_dbs')->getDb('apidefinition','message',$actionName.'_'.$controllerName.'_'.$moduleName);
    $db->insert(self::$_dbMessageTableName, $data); 	
  	return true;
  	
  } //end create

  public function update($messageDefinition,$messageFormat,$changeDescription) {
    if('serializedPHP' == $messageFormat) {
      $message = @unserialize($messageDefinition);
      if(empty($message)) {
      	require_once('apidefinition/objects/Exception.php');
  	    throw new APIDefinition_Object_Exception('Issue decoding message',18); 
      } //Issue decoding message
    } elseif('JSON' == $messageFormat) {
      $message = @json_decode($messageDefinition);
      if(empty($message)) {
      	require_once('apidefinition/objects/Exception.php');
  	    throw new APIDefinition_Object_Exception('Issue decoding message',18); 
      } //Issue decoding message    	
    }

  	//We clean the description, only constraint is that it should be valid HTML
	require_once('htmlpurifier/library/HTMLPurifier.auto.php');
  	$purifier = new HTMLPurifier();
    $description = $purifier->purify($changeDescription);

    //We validate the message definition
    
    $result = $this->_validateMessage($message);
    if(!is_bool($result)) {
      	require_once('apidefinition/objects/Exception.php');
  	    throw new APIDefinition_Object_Exception('Message error: '.$result,19);    	
    }
    
    //OK we can create the Message
	$email = Zend_Registry::get('app_user')->getEmail();
  	$creationDate = time();
  	
  	
  	$data = array(
      self::$_dbMessageActionNameCol => $this->_actionName,
      self::$_dbMessageControllerNameCol => $this->_controllerName,
      self::$_dbMessageModuleNameCol => $this->_moduleName,
      self::$_dbMessageDescriptionCol => $description,
      self::$_dbMessageCreatorEmailCol => $email,
      self::$_dbMessageCreationDateCol => $creationDate,
      self::$_dbMessageMessageCol => self::encodeMessage($message)
  	);
    $db = Zend_Registry::get('dt_dbs')->getDb('apidefinition','message',$this->_actionName.'_'.$this->_controllerName.'_'.$this->_moduleName);
    $db->insert(self::$_dbMessageTableName, $data); 	
  	return true;  	
  }


  /*  make the message the current version
  *@param void
  *@return void
  *@throw APIDefinition_Object_Exception if an issue occurs
  */
  public function makeCurrent() {
    
    Zend_Registry::get('dt_log')->log('Changing current version for the message', Zend_Log::DEBUG);
  	
  
  	$previousCurrentMessage = new APIDefinition_Object_Message($this->getActionName(),$this->getControllerName(),$this->getModuleName(),'','current');
  	Zend_Registry::get('dt_log')->log('Previous current version found, version:'.$previousCurrentMessage->getVersionNumber().' id:'.$previousCurrentMessage->getId(), Zend_Log::DEBUG);
  	
  	//We check that the id of the current message is higher than the last current version (can t promote an older message)
  	if($previousCurrentMessage->getId() >= $this->getId()) {
      	require_once('apidefinition/objects/Exception.php');
  	    throw new APIDefinition_Object_Exception('Previous message with status current has a bigger id than candidate message',22);   			
  	}	
  	
  	//We update the version of the message
  	$previousVersion = $previousCurrentMessage->getVersionNumber();
  	if(!is_numeric($previousVersion)) {
      	require_once('apidefinition/objects/Exception.php');
  	    throw new APIDefinition_Object_Exception('Version is not a number',21);   		
  	}
  	$newVersion = $previousVersion + 1;
  	
    $db = Zend_Registry::get('dt_dbs')->getDb('apidefinition','message',$this->getActionName().'_'.$this->getControllerName().'_'.$this->getModuleName());
 
    $data = array(
          self::$_dbMessageStatusCol      => 'current',
          self::$_dbMessageVersionNumberCol     => $newVersion
    );
    $n = $db->update(self::$_dbMessageTableName, $data, self::$_dbMessageIdCol.' = '.$this->getId());
  	if(1 != $n) {
      	require_once('apidefinition/objects/Exception.php');
  	    throw new APIDefinition_Object_Exception('Issue occured while updating record',22);   		
  	}    
    
  	//We move the version of the previous message to supported
    $data = array(
          self::$_dbMessageStatusCol      => 'supported'
    );
    $n = $db->update(self::$_dbMessageTableName, $data, self::$_dbMessageIdCol.' = '.$previousCurrentMessage->getId());
  	if(1 != $n) {
      	require_once('apidefinition/objects/Exception.php');
  	    throw new APIDefinition_Object_Exception('Issue occured while updating record',22);   		
  	} 
  	
  	  	
  	//We update the current object
  	$this->_status = 'current';
  	$this->_versionNumber = $newVersion;
  	
  	//we return 
  	return;
  		
  }


  /*deprecate the message (change the status from supported to deprecated)
  *@param void
  *@return void
  *@throw APIDefinition_Object_Exception if an issue occurs
  */
  public function deprecate() {
    
    Zend_Registry::get('dt_log')->log('deprecating message', Zend_Log::DEBUG);
  
  	
  	//We check if the current message is supported
  	if($this->getStatus() != 'supported') {
      	require_once('apidefinition/objects/Exception.php');
  	    throw new APIDefinition_Object_Exception('The message is not a supported one and cannot be deprecated',23);   			
  	}	
  	
    $db = Zend_Registry::get('dt_dbs')->getDb('apidefinition','message',$this->getActionName().'_'.$this->getControllerName().'_'.$this->getModuleName());
 
    $data = array(
          self::$_dbMessageStatusCol      => 'deprecated'
    );
    $n = $db->update(self::$_dbMessageTableName, $data, self::$_dbMessageIdCol.' = '.$this->getId());
  	if(1 != $n) {
      	require_once('apidefinition/objects/Exception.php');
  	    throw new APIDefinition_Object_Exception('Issue occured while updating record',22);   		
  	}
  	
  	  	
  	//We update the current object
  	$this->_status = 'deprecated';
  	
  	//we return 
  	return;
  		
  }

  /* formatExampleFromJSON return the PHP object representing the provided JSON string
   *@param JSON string
   *@return PHP array if OK
   * @throw
  */
  public static function formatExampleFromJSON($exampleJSONString) {
      $message = @json_decode($exampleJSONString,true);
      if(empty($message)) {
      	require_once('apidefinition/objects/Exception.php');
  	    throw new APIDefinition_Object_Exception('Issue decoding message',18); 
      } //Issue decoding message
      
      return $message;    	
  }

  /* validate that the message has a valid format
   *@return boolean true if message is correctly defined, error message else
  */ 
  protected function _validateMessage($message) {
    if(!isset($message['inputDefinition']) || !isset($message['outputDefinition']) ) return 'input and/or output definition is missing';
    return true;	
  }
  
  
  static protected function	_validateName($name) {
    
    if(empty($name)) {
  	  require_once('apidefinition/objects/Exception.php');
  	  throw new APIDefinition_Object_Exception('Mandatory data is missing',1);
    }
    
    //Message and module name can only be lowercase alphabet characters and less or equal 32 characters
  	if(preg_match('/[^a-z]/',$name)) {
  	  require_once('apidefinition/objects/Exception.php');
  	  throw new APIDefinition_Object_Exception('Message, controller or module name can only be alphabet lower case characters',2);  		
  	}
  	if(32 < strlen($name)) {
  	  require_once('apidefinition/objects/Exception.php');
  	  throw new APIDefinition_Object_Exception('Message,controller or module name must be less than 33 characters',3);
  	}
  	return true;	
  }
}